use cmsis_rtos2_rs::{osWaitForever, static_mem::{mq_size, EventFlags, MessageQueue, MutexValue, Task, Thread}};
use utils::{defer_ref, log, ufmt};

use super::driver::{ctrl, sensor, APP_INFO};

#[derive(Clone, Copy, ufmt::derive::uDebug)]
pub enum State {
    Stop,
    Exec,
    Timeout,
}

impl Into<u8> for State {
    fn into(self) -> u8 {
        match self {
            Self::Stop => 0,
            Self::Exec => 1,
            Self::Timeout => 2,
        }
    }
}

enum Error {
    StateUpdate(State),
    Timeout,
}

const MSG_COUNT: usize = 2;
const FLAG_STOP: u32 = 0x01;

struct DrawWaterTask {
    mq: MessageQueue<State, MSG_COUNT, { mq_size::<State>(MSG_COUNT) }>,
    state: MutexValue<State>,
    flags: EventFlags,
}

impl DrawWaterTask {

    const fn uninit() -> Self {
        Self { 
            mq: MessageQueue::uninit(),
            state: MutexValue::uninit(State::Exec),
            flags: EventFlags::uninit(),
        }
    
    }

    unsafe fn init_once(&self) {
        self.mq.init_once("draw task");
        self.state.init_once("draw task");
        self.flags.init_once("draw task");
    }
}

static THREAD: Thread<1024> = Thread::uninit();
static TASK: DrawWaterTask = DrawWaterTask::uninit();

pub unsafe fn start() {
    TASK.init_once();
    THREAD.spawn(&TASK);
}

pub fn ctrl(state: State) {
    _ = TASK.mq.post(&state);
}

pub fn stop() {
    _ = TASK.flags.clear_flags(FLAG_STOP);
    ctrl(State::Stop);
    _ = TASK.flags.wait_any_flags(FLAG_STOP, osWaitForever);
}

pub fn state() -> State {
    *TASK.state.lock()
}

impl Task for DrawWaterTask {
    fn run(&self) {
        let mut state = *self.state.lock();
        loop {
            log!("draw:{:?}", state);
            let ret = match state {
                State::Exec => self.do_exec(),
                State::Timeout => self.do_stop(),
                State::Stop => self.do_stop(),
            };
            state = match ret {
                Ok(_) => state,
                Err(Error::StateUpdate(s)) => s,
                Err(Error::Timeout) => State::Timeout,
            };
            *self.state.lock() = state;
        }
    }
}

impl DrawWaterTask {

    fn do_exec(&self) -> Result<(), Error> {
        let timeout = APP_INFO.lock().draw_water_timeout as u32 * 1000;
        let mut time_count = 0;

        defer_ref!({
            ctrl::v7_close();
        });
        
        loop {
            if !sensor::is_water_high() {
                ctrl::v7_open();
                time_count += 50;
                if time_count >= timeout {
                    return Err(Error::Timeout);
                }
            } else {
                ctrl::v7_close();
                time_count = 0;
            }
            self.wait(50)?;
        }
    }

    fn do_stop(&self) -> Result<(), Error> {
        self.flags.set_flags(FLAG_STOP);
        self.wait(osWaitForever)
    }

    fn wait(&self, timeout: u32) -> Result<(), Error> {
        if let Ok(v) = self.mq.poll(timeout) {
            Err(Error::StateUpdate(v))
        } else {
            Ok(())
        }
    }

}








